﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace KaroEngine
{
    public class MinMax : AI
    {
        private Player playerToEvaluate;
        private int countMin;
        private int countMax;
        
        public Move MaxValue(Move move, short depth, short alpha, short beta)
        {
            Engine.currentBoard.DoMove( move );
            
            if (Engine.currentBoard.IsTerminal() || depth == 0)
            {
                short stateEvaluation = Engine.currentBoard.Evaluate(playerToEvaluate);

                #region debug
                // This is for easy debugging
                //Console.WriteLine( "countMax: " + countMax );
                //Console.WriteLine( "Move: " + move );
                //Console.WriteLine( "Evaluation: " + stateEvaluation );
                //Console.WriteLine( Engine.currentBoard );
                //Console.WriteLine(  );
                #endregion

                Engine.currentBoard.UnDoMove();
                countMax++;

                move.Evaluation = stateEvaluation;

                return move;
            }
            else
            {
                Move maxMove = new Move(-1, -1, -1) { Evaluation = short.MinValue };

                foreach (Move nextMove in GetSortedMoves())
                {
                    // if there is such an entry in the transposition table
                    if (TranspositionTable.Instance.IsInTranspositionTable(Engine.currentBoard, nextMove))
                        nextMove.Evaluation = TranspositionTable.Instance.ReadFromTranspositionTable
                            (TranspositionTable.Instance.GetZobristHash(
                            Engine.currentBoard, nextMove));
                    // if not
                    else
                    {
                        nextMove.Evaluation = MinValue(nextMove, (short)(depth - 1), alpha, beta).Evaluation;

                        TranspositionTable.Instance.StoreInTranspositionTable(
                                TranspositionTable.Instance.GetZobristHash(Engine.currentBoard,
                                                                            nextMove),
                                nextMove.Evaluation,
                                (short)(depth - 1));
                    }

                    if (nextMove.Evaluation > maxMove.Evaluation)
                    {
                        maxMove = nextMove;
                        alpha = nextMove.Evaluation;
                    }
                    if (alpha <= beta)
                    {
                        Engine.currentBoard.UnDoMove();

                        return maxMove;
                    }
                }

                Engine.currentBoard.UnDoMove();

                return maxMove;
            }
        }

       private Move MinValue(Move move, short depth, short alpha, short beta)
        {
            Engine.currentBoard.DoMove( move );

            if (Engine.currentBoard.IsTerminal() || depth == 0)
            {
                short stateEvaluation = Engine.currentBoard.Evaluate(playerToEvaluate);

                #region debug
                // This is for easy debugging
                //Console.WriteLine( "countMin: " + countMin );
                //Console.WriteLine( "Move: " + move );
                //Console.WriteLine( "Evaluation: " + stateEvaluation );
                //Console.WriteLine( Engine.currentBoard );
                //Console.WriteLine(  );
                #endregion

                Engine.currentBoard.UnDoMove();
                countMin++;

                move.Evaluation = stateEvaluation;

                return move;
            }
            else
            {
                Move minMove = new Move( -1, -1, -1 ) { Evaluation = short.MaxValue };

                foreach (Move nextMove in GetSortedMoves())
                {
                    ////if there is such an entry in the transposition table
                    if (TranspositionTable.Instance.IsInTranspositionTable(Engine.currentBoard, nextMove))
                        nextMove.Evaluation = TranspositionTable.Instance.ReadFromTranspositionTable(
                                                TranspositionTable.Instance.GetZobristHash(Engine.currentBoard,
                                                nextMove));
                    // if not
                    else
                    {
                    nextMove.Evaluation = MaxValue( nextMove, (short) ( depth - 1 ), alpha, beta ).Evaluation;

                        TranspositionTable.Instance.StoreInTranspositionTable(
                                TranspositionTable.Instance.GetZobristHash( Engine.currentBoard, nextMove ),
                                nextMove.Evaluation,
                                (short) ( depth - 1 ) );
                    }

                    if (nextMove.Evaluation < minMove.Evaluation)
                    {
                        minMove = nextMove;
                        beta = nextMove.Evaluation;
                    }
                    if (alpha >= beta)
                    {
                        Engine.currentBoard.UnDoMove();

                        return minMove;
                    }
                }

                Engine.currentBoard.UnDoMove();

                return minMove;
            }
        }

       private List<Move> GetSortedMoves()
       {
           List<Move> moves = Engine.currentBoard.GetPossibleMoves();

           List<KeyValuePair<short,Move>> sortedMoves = new List<KeyValuePair<short, Move>>();

           foreach ( Move move in moves )
           {
               Engine.currentBoard.DoMove( move );
               short eval = Engine.currentBoard.Evaluate( playerToEvaluate );
               
               sortedMoves.Add( new KeyValuePair<short, Move>(eval,move) );

               Engine.currentBoard.UnDoMove();
           }

           List<Move> newMoves = ( from n in sortedMoves
                    orderby n.Key descending
                    select n.Value ).ToList();

           return newMoves;
       }

        public Move GetBestMove()
        {
            playerToEvaluate = Engine.currentBoard.CurrentPlayer;
            return MaxValue(new Move(-1, -1, -1), Engine.aiDepth, short.MinValue, short.MaxValue);
        }
    }
}
